﻿var CanvasTool = (function () {

    var MAX_DISTANCE = 15,
        ALPHA_DECREASE = 0.001;

    var canvas,
        context,
        isDrawing = false,
        currentColor,
        currentSize,
        isPointerOut = false,
        prevX,
        prevY,
        currentBrush,
        originalBrush,
        maxDistance;

    function init(options) {

        canvas = options.canvas;
        currentColor = options.color;
        currentSize = options.size;

        canvas.setAttribute("width", canvas.clientWidth);
        canvas.setAttribute("height", canvas.clientHeight);

        canvas.addEventListener("MSPointerDown", _handlePointerDown);
        canvas.addEventListener("MSPointerMove", _handlePointerMove);
        canvas.addEventListener("MSPointerUp", _handlePointerUp);
        canvas.addEventListener("MSPointerOut", _handlePointerOut);

        context = canvas.getContext("2d");

        originalBrush = new Image();
        originalBrush.src = "/images/brush_water.png";
        currentBrush = originalBrush;

        maxDistance = MAX_DISTANCE;
    }

    function _handlePointerDown(e) {
        isDrawing = true;
        isPointerOut = false;

        context.lineWidth = currentSize;
        context.fillStyle = currentColor;
        context.strokeStyle = currentColor;

        prevX = e.currentPoint.position.x;
        prevY = e.currentPoint.position.y;
    }

    function _handlePointerMove(e) {
        if (isPointerOut) {
            isPointerOut = false;
        }
        if (isDrawing) {
            var x = e.currentPoint.position.x;
            var y = e.currentPoint.position.y;

            if (prevX == undefined) {
                prevX = x;
                prevY = y;
            }

            drawPoints(prevX, prevY, x, y, maxDistance);

            prevX = x;
            prevY = y;
        }
    }

    function _handlePointerUp(e) {
        isDrawing = false;
    }

    function _handlePointerOut(e) {
        isPointerOut = true;

        prevX = undefined;
        prevY = undefined;
    }

    function convertToRgbaObject(rgba) {
        var regex = /^rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(-*\d*.*\d*)\)$/,
            bits = regex.exec(rgba);

        if (bits == null)
            return { r: 0, g: 0, b: 0, a: 0 };

        var r = bits.length > 0 ? parseInt(bits[1]) : 0;
        var g = bits.length > 1 ? parseInt(bits[2]) : 0;
        var b = bits.length > 2 ? parseInt(bits[3]) : 0;
        var a = bits.length > 3 ? parseFloat(bits[4]) : 0;

        return { r: r, g: g, b: b, a: a };
    }

    function applyColor(image, color) {
        var canvas = document.createElement("canvas");
        canvas.width = image.width
        canvas.height = image.height;

        var context = canvas.getContext("2d");
        context.drawImage(image, 0, 0);

        var pixels = context.getImageData(0, 0, image.width, image.height);

        var rgba = convertToRgbaObject(color);

        for (var y = 0; y < pixels.height; y++) {
            for (var x = 0; x < pixels.width; x++) {
                var i = (y * 4) * pixels.width + x * 4;
                pixels.data[i + 0] = rgba.r;
                pixels.data[i + 1] = rgba.g;
                pixels.data[i + 2] = rgba.b;
            }
        }

        context.putImageData(pixels, 0, 0, 0, 0, pixels.width, pixels.height);

        return canvas.toDataURL();
    }

    function drawPoints(x1, y1, x2, y2, max_distance) {

        var dx = (x1 - x2);
        var dy = (y1 - y2);
        var distance = Math.sqrt((dx * dx) + (dy * dy));

        if (distance <= max_distance) {
            drawPoint(x2, y2);
            return;
        }

        var midx = (x1 + x2) / 2;
        var midy = (y1 + y2) / 2;

        drawPoints(x1, y1, midx, midy, max_distance);
        drawPoints(midx, midy, x2, y2, max_distance);
    }

    function drawPoint(x, y) {
        context.drawImage(currentBrush, x - currentSize / 2, y - currentSize / 2, currentSize, currentSize);
        context.globalAlpha -= ALPHA_DECREASE;
    }

    function setBrushSize(value) {
        currentSize = value;
    }

    function setColor(value) {
        context.globalAlpha = 1;
        currentColor = value;
        currentBrush.src = applyColor(originalBrush, currentColor);
    }

    function setBrushDistance(value) {
        maxDistance = value;
    }

    function setBrush(value) {
        originalBrush.src = value;
        currentBrush.src = value;
    }

    function clear() {
        context.clearRect(0, 0, canvas.width, canvas.height);
    }

    return {
        init: init,
        setBrushSize: setBrushSize,
        clear: clear,
        setColor: setColor,
        setBrushDistance: setBrushDistance,
        setBrush: setBrush
    };
})();